package Or

import (
	"encoding/json"
	"errors"
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"strings"
	"sync/atomic"
	"unsafe"
)

type TableRows struct{}

/*
Sql Oracle 生成查询数据的sql语句
*/
func (or TableRows) Sql(s global.StartPart) (selectSql string, err error) {
	var (
		event = "[oracleSql]"
		res   colAndWhereResult
		w     = []string{" "}
	)
	if len(s.TableInfo.Schema) == 0 || len(s.TableInfo.BackendTableName) == 0 {
		err = errors.New(fmt.Sprintf("%v sql general fail. schema or table is entry. Execution process:{schema:%v,table:%v}", event, s.TableInfo.Schema, s.TableInfo.BackendTableName))
		return "", err
	}
	if res, err = colAndWhere(s); err != nil {
		return
	}
	selectSql = fmt.Sprintf("select %v %v \n"+
		"from %v %v T \n"+
		"where 1= 1 %v \n"+
		"%v",
		hitSpecify(s), strings.Join(res.selectColumn, ","),
		tableObject(s.TableInfo.Schema, s.TableInfo.BackendTableName, s.TableInfo.PartitionName), specifyScn(s.Input.Scn),
		strings.Join(append(w, res.whereCondition...), " and "),
		res.orderByColumn)
	return selectSql, nil
}

// specialTypeIf 判断是否是特殊类型
func specialTypeFilter(columnMet []global.TableMeta) (res []global.TableMeta) {
	for _, v := range columnMet {
		switch v.ConvertColumnType.Type {
		case "gis": //地图类型需要特殊处理
			res = append(res, v)
		}
	}
	return
}

func jsonStrActive(s string) (r string) {
	r = s
	if !strings.HasPrefix(s, "{") {
		if n := strings.Index(s, "{"); n != -1 {
			r = s[n:]
		}
	}
	r = strings.ReplaceAll(r, "nil", "null")
	return
}
func jsonToStruct(s string, data any) (err error) {
	// 解析 JSON 字符串
	err = json.Unmarshal([]byte(jsonStrActive(s)), data)
	if err != nil {
		return
	}
	return
}
func structToJson(s any) (r string, err error) {
	var (
		res []byte
	)
	if res, err = json.Marshal(s); err != nil {
		return
	}
	r = string(res)
	return
}
func SdoGType(geo SDOGeometry) (r string, err error) {
	switch geo.SDOGType {
	//基本几何类型
	case "2001": //点（Point）
		geo.SDOPoint.RID = geo.SDOSRID
		if r, err = structToJson(geo.SDOPoint); err != nil {
			return
		}
		r = fmt.Sprintf("POINT@%v", r)
	case "2002": //线（LineString）
	case "2003": //多边形（Polygon）
	//复合几何类型
	case "3001": //多点（MultiPoint）
	case "3002": //多线（MultiLineString）
	case "3003": //多多边形（MultiPolygon）
	//其他几何类型
	case "2004": //圆（Circular String）
	case "2005": //曲线（Curve）
	case "2006": //多曲线
	case "2007": //多线体
	//复杂几何类型
	case "3004": //多圆（MultiCircular String）
	case "3005": //多曲线（MultiCurve）
	case "3006": //多表面（MultiSurface）
	//复合类型示例
	case "2000": //表示所有几何类型
	case "4000": //表示所有复合几何类型
	}
	return
}
func gisTypeActive(s string, details global.TableMeta) (r string, err error) {
	switch strings.ToUpper(details.DataType) {
	case "SDO_GEOMETRY":
		// 创建 SDOGeometry 实例
		var geometry SDOGeometry
		if jsonToStruct(s, &geometry) != nil {
			return
		}
		if r, err = SdoGType(geometry); err != nil {
			return
		}
	case "ST_GEOMETRY":
		// 创建 SDOGeometry 实例
		var geometry STGeometry
		if jsonToStruct(s, &geometry) != nil {
			return
		}
		if r, err = SdoGType(geometry.GEOM); err != nil {
			return
		}
	}
	return
}

func specialTypeValueActive(s string, details global.TableMeta) (r string, err error) {
	switch details.ConvertColumnType.Type {
	case "gis":
		return gisTypeActive(s, details)
	}
	return
}
func nullValueActive(lob bool, s string, details global.TableMeta) (r string) {
	switch {
	case strings.EqualFold(s, "<null>"):
		if lob { //json数据的null值在lob插入是nil
			return fmt.Sprintf("<prepare nil>")
		}
		return fmt.Sprintf("NULL")
	case strings.EqualFold(s, "<entry>") || len(s) == 0:
		switch strings.ToLower(details.ConvertColumnType.Type) {
		case "gis", "decimal", "int":
			return fmt.Sprintf("NULL")
		default:
			if lob {
				return fmt.Sprintf("<prepare entry>")
			}
			return fmt.Sprintf("''")
		}
	default:
		return
	}
}
func specialTypeInsertActive(lob bool, s string, details global.TableMeta) (r string) {
	if len(nullValueActive(lob, s, details)) > 0 {
		return nullValueActive(lob, s, details)
	}
	switch {
	case strings.EqualFold(details.ConvertColumnType.Type, "time"):
		return fmt.Sprintf("'%v'", s)
	case strings.EqualFold(details.ConvertColumnType.Type, "gis"):
		//return gisTypeActive(s)
	default:
		r = s
		switch {
		case strings.EqualFold(details.DataType, "json") && lob:
			return fmt.Sprintf("%v", s)
		default:
			if !lob {
				return fmt.Sprintf("'%v'", s)
			}
		}
	}
	return
}

// specialTypeDataProcess 是否对特殊类型的数据进行处理
func specialTypeDataProcess(data any, columnMet []global.TableMeta) (rowsData any, err error) {
	if data == nil {
		return
	}
	tableData := data.([][]*string)
	filter := specialTypeFilter(columnMet)
	if len(filter) == 0 {
		return
	}
	for k, v := range tableData { //多行
		for j, g := range v { //单行
			for _, n := range filter {
				if strings.EqualFold(fmt.Sprintf("%v", j+1), n.ColumnSeq) {
					var z string
					if z, err = specialTypeValueActive(*g, n); err != nil {
						return
					}
					atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&g)), unsafe.Pointer(&z))
					tableData[k][j] = g
				}
			}
		}
	}
	rowsData = tableData
	return
}

/*
Data Oracle 通过where条件查询表的分段数据（查询数据生成带有greatDbCheck标识的数据块）
*/
func (or TableRows) Data(s global.RowsDataInput) (result global.ExecSqlResult, err error) {
	var event = "[oracleData]"
	result.ExecSql = s.Sql
	if result.Timeout, result.Result, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: s.Sql,
		Expect:       ETL.GarthStringPointGarth,
	}); err != nil {
		err = errors.New(fmt.Sprintf("%v %v", event, err))
		return
	}
	return
}

func (or TableRows) MissData(s global.RowsDataInput) (result global.ExecSqlResult, err error) {
	var (
		event = "[oracleMissData]"
		res   colAndWhereResult
	)
	s.SelectColumn.OutputColumnType = "all"
	if res, err = colAndWhere(global.StartPart{
		SelectColumn: s.SelectColumn,
	}); err != nil {
		return
	}
	result.ExecSql = fmt.Sprintf("select %v %v \n"+
		"from %v %v T \n"+
		"where 1 = 1 %v ",
		parallel(s.Input.TablePx), strings.Join(res.selectColumn, ","),
		tableObject(s.TableInfo.Schema, s.TableInfo.BackendTableName, s.TableInfo.PartitionName), specifyScn(s.Input.Scn),
		rowsLimitC(s.TableInfo.RowsLimit))
	if result.Timeout, result.Result, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: result.ExecSql,
		Expect:       ETL.FlowAny,
	}); err != nil {
		return
	}
	return
}
